﻿''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Magic Content - A desktopmodule to publish content and send out
' newsletters from within the DotNetNuke® Portal Framework
'
'
' Copyright(c) 2005-2006 proeder.network.consult
' Philipp Becker (philipp.becker@portalfabrik.net)
' Internet: http://www.portalfabrik.net
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Imports System
Imports System.ComponentModel
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Text
Imports System.Web.UI
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Xml

Imports DotNetNuke

Imports DotNetNuke.Common
Imports DotNetNuke.Common.Utilities

Imports DotNetNuke.Entities.Users
Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Portals
Imports DotNetNuke.Entities.Tabs

Imports DotNetNuke.Security
Imports DotNetNuke.Security.Roles

Imports DotNetNuke.Services.Localization

Namespace dnnWerk.Modules.Nuntio.Content

    Public Class ContentProcessor

        Inherits ContentBase

#Region "Private Members"
        Private _objUser As UserInfo
        Private _objPortal As PortalSettings
        Private _Content As String
        Private _ParsedContent As String
        Private _ProcessMode As String
        Private _SrcDir As String
        Private _ModID As Integer
        Private _msettings As Hashtable
#End Region

#Region "Constructors"

        Public Sub New()
        End Sub

        Public Sub New(ByVal Content As String, ByVal ProcessMode As String, ByVal Resourcefile As String, ByVal SrcDir As String, ByVal Portalsettings As PortalSettings, ByVal ModID As Integer, ByVal Settings As Hashtable)
            _Content = Content
            _ProcessMode = ProcessMode
            Me.LocalResourceFile = Resourcefile
            _SrcDir = SrcDir
            _objPortal = Portalsettings
            _ModID = ModID
            _msettings = Settings
        End Sub

        Public Sub New(ByVal objUser As UserInfo, ByVal Content As String, ByVal ProcessMode As String, ByVal Resourcefile As String, ByVal SrcDir As String, ByVal Portalsettings As PortalSettings, ByVal ModID As Integer, ByVal Settings As Hashtable)
            _objUser = objUser
            _Content = Content
            _ProcessMode = ProcessMode
            Me.LocalResourceFile = Resourcefile
            _SrcDir = SrcDir
            _objPortal = Portalsettings
            _ModID = ModID
            _msettings = Settings
        End Sub

#End Region

#Region "Public Properties"

        Public Property mSettings() As Hashtable
            Get
                Return _msettings
            End Get
            Set(ByVal value As Hashtable)
                _msettings = value
            End Set
        End Property

        Public Property ModID() As Integer
            Get
                Return _ModID
            End Get
            Set(ByVal Value As Integer)
                _ModID = Value
            End Set
        End Property

        Public Property objPortal() As PortalSettings
            Get
                Return _objPortal
            End Get
            Set(ByVal Value As PortalSettings)
                _objPortal = Value
            End Set
        End Property

        Public Property objUserObject() As UserInfo
            Get
                Return _objUser
            End Get
            Set(ByVal Value As UserInfo)
                _objUser = Value
            End Set
        End Property

        Public Property SrcDir() As String
            Get
                Return _SrcDir
            End Get
            Set(ByVal Value As String)
                _SrcDir = Value
            End Set
        End Property

        Public Property Content() As String
            Get
                Return _Content
            End Get
            Set(ByVal Value As String)
                _Content = Value
            End Set
        End Property

        Public Property ParsedContent() As String
            Get
                Return _ParsedContent
            End Get
            Set(ByVal Value As String)
                _ParsedContent = Value
            End Set
        End Property

        Public Property ProcessMode() As String
            Get
                Return _ProcessMode
            End Get
            Set(ByVal Value As String)
                _ProcessMode = Value
            End Set
        End Property

#End Region

#Region "Body Replace Methods"

        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
        '
        ' methods that replace tokens in the content string
        ' these methods are called from the ProcessBody method
        '
        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

        Private Function BodyReplace_Time(ByVal Type As String) As String

            Dim MyTime As Date

            Try
                Select Case LCase(Type)
                    Case "user"
                        MyTime = DateAdd(DateInterval.Minute, CType(GetUserTime(), Double), Now.ToUniversalTime())
                    Case "server"
                        MyTime = DateAdd(DateInterval.Minute, CType(Me.objPortal.TimeZoneOffset, Double), Now.ToUniversalTime())
                End Select

                Return MyTime.ToShortTimeString


            Catch ex As Exception
                Return Nothing
            End Try

        End Function

        Private Function BodyReplace_Portal(ByVal Type As String) As String

            Try
                If Not objPortal.PortalId = -1 Then
                    Select Case LCase(Type)
                        Case "email"
                            Return objPortal.Email
                        Case "name"
                            Return objPortal.PortalName
                        Case "currency"
                            Return objPortal.Currency
                        Case "description"
                            Return objPortal.Description
                    End Select
                End If
            Catch ex As Exception
                Return ""
            End Try
        End Function

        Private Function BodyReplace_Username(ByVal Type As String) As String
            Try
                If Not objUserObject.UserID = -1 Then
                    Select Case LCase(Type)
                        Case "username"
                            Return objUserObject.Username
                        Case "fullname", "displayname"
                            Return objUserObject.DisplayName
                        Case "firstname"
                            Return objUserObject.FirstName
                        Case "lastname"
                            Return objUserObject.LastName
                        Case "email"
                            Return objUserObject.Membership.Email
                    End Select
                Else
                    Return Localization.GetString("txtGenericUser.Text", Me.LocalResourceFile)
                End If
            Catch ex As Exception
                Return ""
            End Try

        End Function

        Private Function BodyReplace_CurrentDate(ByVal Type As String) As String
            Try
                Select Case LCase(Type)
                    Case "long"
                        Return Now.ToLongDateString
                    Case "short"
                        Return Now.ToShortDateString
                End Select

            Catch ex As Exception
                Return ""
            End Try

        End Function

        Private Function BodyReplace_LoginLink() As String

            Dim strLoginOutLink As String

            Try
                '
                If Not objUserObject.UserID = -1 Then
                    'logged on user, show logoff link

                    If DotNetNuke.Entities.Host.HostSettings.GetHostSetting("UseFriendlyUrls") = "Y" Then
                        strLoginOutLink = "<a href=""" & FriendlyUrl(objPortal.ActiveTab, ApplicationURL(objPortal.ActiveTab.TabID) & "&portalid=" & objPortal.PortalId.ToString, "Logoff.aspx") & """>" & Localization.GetString("txtLogoff.Text", Me.LocalResourceFile) & "</a>"
                    Else
                        strLoginOutLink = "<a href=""" & ResolveUrl("~/Admin/Security/Logoff.aspx?tabid=" & objPortal.ActiveTab.TabID & "&portalid=" & objPortal.PortalId.ToString) & """>" & Localization.GetString("txtLogoff.Text", Me.LocalResourceFile) & "</a>"
                    End If
                Else
                    'user is not logged on, show login in
                    If objPortal.LoginTabId <> -1 Then
                        ' user defined tab
                        strLoginOutLink = "<a href=""" & NavigateURL(objPortal.LoginTabId) & """>" & Localization.GetString("txtLogin.Text", Me.LocalResourceFile) & "</a>"
                    Else
                        ' admin tab
                        strLoginOutLink = "<a href=""" & NavigateURL("Login") & """>" & Localization.GetString("txtLogin.Text", Me.LocalResourceFile) & "</a>"
                    End If
                End If

                If ProcessMode = "newsletter" Then
                    strLoginOutLink = ""
                End If

            Catch ex As Exception
                Return ""
            End Try

            Return strLoginOutLink

        End Function

        Private Function BodyReplace_ProfileLink() As String
            Dim strProfileLink As String

            Try
                If objUserObject.UserID = -1 Then
                    If objPortal.UserRegistration <> 0 Then
                        If objPortal.UserTabId <> -1 Then
                            ' user defined tab
                            strProfileLink = "<a href=""" & NavigateURL(objPortal.UserTabId) & """>"
                        Else
                            ' admin tab
                            strProfileLink = "<a href=""" & NavigateURL("Register") & """>"
                        End If
                        strProfileLink = strProfileLink & Localization.GetString("txtRegister.Text", Me.LocalResourceFile) & "</a>"
                    End If
                Else
                    If objPortal.UserTabId <> -1 Then
                        strProfileLink = "<a href=""" & NavigateURL(objPortal.UserTabId) & """>"
                    Else
                        strProfileLink = "<a href=""" & NavigateURL("Profile", "UserID=" & UserInfo.UserID.ToString) & """>"
                    End If
                    strProfileLink = strProfileLink & Localization.GetString("txtProfile.Text", Me.LocalResourceFile) & "</a>"
                End If


                If ProcessMode = "newsletter" Then
                    strProfileLink = """"
                End If

            Catch ex As Exception

                Return ""

            End Try

            Return strProfileLink


        End Function

        Private Function BodyReplace_DayTime(ByVal Type As String) As String


            Dim MyTime As Date
            Dim strHTML As String

            Try
                Select Case LCase(Type)
                    Case "user"
                        MyTime = DateAdd(DateInterval.Minute, CType(GetUserTime(), Double), Now.ToUniversalTime())
                    Case "server"
                        MyTime = DateAdd(DateInterval.Minute, CType(Me.objPortal.TimeZoneOffset, Double), Now.ToUniversalTime())
                End Select




                If Hour(MyTime) < 12 Then
                    strHTML = Localization.GetString("txtMorning.Text", Me.LocalResourceFile)
                ElseIf Hour(MyTime) >= 12 And Hour(MyTime) < 18 Then
                    strHTML = Localization.GetString("txtAfternoon.Text", Me.LocalResourceFile)
                Else
                    strHTML = Localization.GetString("txtEvening.Text", Me.LocalResourceFile)
                End If
            Catch ex As Exception
                Return Localization.GetString("txtGeneric.Text", Me.LocalResourceFile)
            End Try

            Return strHTML

        End Function

        Private Function BodyReplace_Clock(ByVal Type As String) As String

            Try
                Select Case LCase(Type)
                    Case "server"
                        Return ShowClock(CType(objPortal.TimeZoneOffset, String))
                    Case "user"
                        Return ShowClock(CType(GetUserTime(), String))
                    Case Else
                        Return ShowClock(Type)
                End Select
            Catch ex As Exception
                Return Nothing
            End Try

        End Function

#End Region

#Region "Public Methods"

        Public Sub Process()


            Select Case Me.ProcessMode

                Case "page"

                    _ParsedContent = ProcessFileToken(Me.Content)
                    _ParsedContent = ParseContent(_ParsedContent)


                Case "newsletter"

                    _ParsedContent = ProcessFileToken(Me.Content)
                    _ParsedContent = ParseContent(_ParsedContent)


                Case "editorpreview"

                    _ParsedContent = ProcessFileToken(Me.Content)

                Case "updatedb"

                    _ParsedContent = ProcessFileLinks(Me.Content)

            End Select

        End Sub

#End Region

#Region "Private Methods"

        Private Function ParseContent(ByVal strHTML As String) As String

            Dim sA As String()
            Dim delimStr As String = "[]"
            Dim delimiter As Char() = delimStr.ToCharArray()
            sA = strHTML.Split(delimiter)


            Dim sb As New StringBuilder
            Dim str As String
            Dim ColEnd As String = ""

            For iPtr As Integer = 0 To sA.Length - 1 Step 2

                sb.Append(sA(iPtr).ToString())

                If iPtr < sA.Length - 1 Then
                    str = sA(iPtr + 1)

                    If str.ToUpper = "LOGINOUT" Then

                        sb.Append(Me.BodyReplace_LoginLink)

                    ElseIf str.ToUpper = "PROFILELINK" Then

                        sb.Append(Me.BodyReplace_ProfileLink)

                    ElseIf str.ToUpper.StartsWith("DAYTIME:") Then

                        Dim DayTimeType As String = sA(iPtr + 1).Substring(8, sA(iPtr + 1).Length - 8)
                        sb.Append(BodyReplace_DayTime(DayTimeType))

                    ElseIf str.ToUpper.StartsWith("CURRENTDATE:") Then

                        Dim DateFormatType As String = sA(iPtr + 1).Substring(12, sA(iPtr + 1).Length - 12)
                        sb.Append(Me.BodyReplace_CurrentDate(DateFormatType))

                    ElseIf str.ToUpper.StartsWith("USER:") Then

                        If Me.ProcessMode = "newsletter" Then
                            sb.Append("[" & str & "]")
                        Else
                            Dim NameType As String = sA(iPtr + 1).Substring(5, sA(iPtr + 1).Length - 5)
                            sb.Append(BodyReplace_Username(NameType))
                        End If


                    ElseIf str.ToUpper.StartsWith("PORTAL:") Then

                        Dim NameType As String = sA(iPtr + 1).Substring(7, sA(iPtr + 1).Length - 7)
                        sb.Append(BodyReplace_Portal(NameType))

                    ElseIf str.ToUpper.StartsWith("CLOCK:") Then

                        Dim ClockType As String = sA(iPtr + 1).Substring(6, sA(iPtr + 1).Length - 6)
                        sb.Append(BodyReplace_Clock(ClockType))

                    ElseIf str.ToUpper = "CLOCK" Then

                        sb.Append(BodyReplace_Clock(""))

                    ElseIf str.ToUpper.StartsWith("TIME:") Then

                        Dim TimeType As String = sA(iPtr + 1).Substring(6, sA(iPtr + 1).Length - 6)
                        sb.Append(BodyReplace_Time(TimeType))

                    Else

                        sb.Append("[" & str & "]")

                    End If

                End If

            Next
            Return sb.ToString

        End Function

        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
        '
        ' replaces links to internal files with a fileid token
        ' that we can parse for at runtime
        ' this a two way function:
        ' when fileid tokens are found they are replaced with the correct file path
        '
        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

        'supports only image links right now

        Private Function ProcessFileToken(ByVal strHTML As String) As String

            'try to decode the string first
            'strHTML = Server.HtmlDecode(strHTML)

            Dim strWork As String = ""
            Dim strFile As String = ""
            Dim posImgStart As Integer
            Dim posImgEnd As Integer
            Dim posNext As Integer = 0
            Dim strImage As String = ""
            Dim FileID As Integer

            'get first position of first image tag
            Dim posImg As Integer = strHTML.IndexOf("<img", 0)

            'loop as long as image tags are found
            While posImg <> -1

                'starting point in string
                posImgStart = strHTML.IndexOf("<img", posImg)

                'strip out tag, starting at first occurence
                strWork = strHTML.Substring(posImgStart)

                'find end of image tag
                posImgEnd = strWork.IndexOf(">")

                'again, strip out mage tag, ending at the last occurence of >
                strWork = strWork.Substring(0, posImgEnd + 1)

                'not needed
                'posNext = posImgStart + posImgEnd

                'now find the beginning of the path in our string
                posImgStart = strWork.IndexOf("src=")
                'define starting point for next stripping point
                strWork = strWork.Substring(posImgStart + 5)

                'find the end of the string to be parsed for in current string
                posImgEnd = strWork.IndexOf("""")

                strWork = strWork.Substring(0, posImgEnd)


                If strWork.IndexOf("[PORTALFILEID=", 0) <> -1 Then

                    strImage = strWork.Replace("[PORTALFILEID=", "")
                    strImage = strImage.Replace("]", "")
                    FileID = Integer.Parse(strImage)
                    'get the correct path
                    strImage = GetFilePath(FileID)
                Else
                    'no internal link, leave the link as is
                    strImage = strWork
                End If

                'replace our work in the html string
                strHTML = strHTML.Replace(strWork, strImage)

                'set start position for next search
                'posNext = strHTML.IndexOf(strImage)

                'set new start position for the next loop otherwise this will never end...
                posImg = strHTML.IndexOf("<img", posImg + 4)
                posImg = posImg

            End While

            Return strHTML

        End Function

        Private Function ProcessFileLinks(ByVal strHTML As String) As String

            'try to decode the string first
            'strHTML = Server.HtmlDecode(strHTML)

            Dim strWork As String = ""
            Dim strFile As String = ""
            Dim posImgStart As Integer
            Dim posImgEnd As Integer
            Dim posNext As Integer = 0
            Dim strImage As String = ""
            Dim FileID As Integer

            'get first position of first image tag
            Dim posImg As Integer = strHTML.IndexOf("<img", 0)

            'loop as long as image tags are found
            While posImg <> -1

                'starting point in string
                posImgStart = strHTML.IndexOf("<img", posImg)

                'strip out tag, starting at first occurence
                strWork = strHTML.Substring(posImgStart)

                'find end of image tag
                posImgEnd = strWork.IndexOf(">")

                'again, strip out mage tag, ending at the last occurence of >
                strWork = strWork.Substring(0, posImgEnd + 1)

                'not needed
                'posNext = posImgStart + posImgEnd

                'now find the beginning of the path in our string
                posImgStart = strWork.IndexOf("src=")
                'define starting point for next stripping point
                strWork = strWork.Substring(posImgStart + 5)

                'find the end of the string to be parsed for in current string
                posImgEnd = strWork.IndexOf("""")

                strWork = strWork.Substring(0, posImgEnd)

                'check if the image tag links to an internal file
                If strWork.ToLower.IndexOf(objPortal.HomeDirectory.ToLower) <> -1 Then
                    'parse out portal home dir
                    Dim intStart As Integer = strWork.ToLower.IndexOf(objPortal.HomeDirectory.ToLower)
                    intStart = intStart + objPortal.HomeDirectory.Length
                    strImage = strWork.Substring(intStart)
                    strImage = GetFileIDString(strImage)
                Else
                    strImage = strWork
                End If

                'replace our work in the html string
                strHTML = strHTML.Replace(strWork, strImage)

                'set start position for next search
                'posNext = strHTML.IndexOf(strImage)

                'set new start position for the next loop otherwise this will never end...
                posImg = strHTML.IndexOf("<img", posImg + 4)
                posImg = posImg

            End While

            Return strHTML

        End Function

        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
        '
        ' gets the file path of an internal link, if no file found 
        ' it returns a reference to a blank file in the module directory
        '
        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

        Private Function GetFilePath(ByVal FileID As Integer) As String

            Dim ImageFile As DotNetNuke.Services.FileSystem.FileInfo
            Dim FileController As New DotNetNuke.Services.FileSystem.FileController
            ImageFile = FileController.GetFileById(FileID, PortalId)
            If Not ImageFile Is Nothing Then
                'fix for sending out content in emails
                Dim strDomainName As String = GetPortalDomainName(objPortal.PortalAlias.HTTPAlias, Nothing, True)
                If Common.Globals.ApplicationPath.Length > 0 Then
                    strDomainName = strDomainName.ToLower.Replace(Common.Globals.ApplicationPath.ToLower, "")
                End If

                Dim blnUseAbsoluePaths As Boolean = False
                If mSettings.Contains("PNC_CONTENT_USEABSOLUTEPATHS") Then
                    If Boolean.Parse(mSettings("PNC_CONTENT_USEABSOLUTEPATHS")) = True Then
                        blnUseAbsoluePaths = True
                    End If
                End If

                'we now only use fully qualified paths if activated
                If blnUseAbsoluePaths Then
                    Return strDomainName & objPortal.HomeDirectory & ImageFile.Folder & ImageFile.FileName
                Else
                    Return AddTrailingSlash(objPortal.HomeDirectory & ImageFile.Folder & ImageFile.FileName)
                End If

            Else
                Return GetPortalDomainName(objPortal.PortalAlias.HTTPAlias, Nothing, True) & "/images/" & "blank.gif"
            End If

        End Function

        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
        '
        ' returns a file id token for db storage
        '
        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

        Private Function GetFileIDString(ByVal strFile As String) As String

            strFile = strFile.Replace("%20", " ")
            Dim objFileController As New DotNetNuke.Services.FileSystem.FileController
            Dim objFile As New DotNetNuke.Services.FileSystem.FileInfo

            objFile = objFileController.GetFile(RemoveTrailingSlash(strFile), PortalId)
            If objFile Is Nothing Then
                Try
                    Me.SynchronizePortalStore(PortalId, objPortal.AdministratorRoleId, objPortal.HomeDirectoryMapPath)
                    'FileSystemUtils.Synchronize(PortalId, objPortal.AdministratorRoleId, objPortal.HomeDirectoryMapPath)
                    objFile = objFileController.GetFile(RemoveTrailingSlash(strFile), PortalId)
                Catch
                End Try
                objFile = objFileController.GetFile(RemoveTrailingSlash(strFile), PortalId)
            End If

            Dim strFileToken As String
            If Not objFile Is Nothing Then
                strFileToken = "[PORTALFILEID=" & objFile.FileId.ToString & "]"
            Else
                strFileToken = strFile
            End If
            Return strFileToken

        End Function

        Private Function AddTrailingSlash(ByVal strFilePath As String) As String

            If strFilePath.IndexOf("/") = 0 Then
                Return strFilePath
            Else
                Return "/" & strFilePath
            End If
        End Function

        Private Function RemoveTrailingSlash(ByVal strFilePath As String) As String

            If strFilePath.IndexOf("/") = 0 Then
                Return strFilePath.Substring(1)
            Else
                Return strFilePath
            End If
        End Function

        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
        '
        ' returns the timezone offset for the current visitor
        '
        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

        Private Function GetUserTime() As Integer

            Dim MyTime As Integer
            If Not objUserObject.UserID = -1 Then

                'get the current hour of the user
                MyTime = objUserObject.Profile.TimeZone

            Else
                'use time zone settings of the portal
                MyTime = objPortal.TimeZoneOffset
            End If

            If TimeZone.CurrentTimeZone.IsDaylightSavingTime(Now) Then
                MyTime = MyTime + TimeZone.CurrentTimeZone.GetUtcOffset(Now).Minutes
            End If

            Return MyTime

        End Function

        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
        '
        ' returns a html string that contains a flash animated clock        
        '
        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

        Private Function ShowClockOld(ByVal MyTime As String) As String
            Dim strClock As String
            Try
                strClock = "<object classid=""clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"" codebase = ""http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=5,0,42,0"" id=""analog_clock"" width=""90"" height=""90"">" & _
                                                             "<param name=""movie"" value=""" & Me.SrcDir & "/analog_clock.swf"">" & _
                                                             "<param name=""bgcolor"" value="""">" & _
                                                             "<param name=""WMode"" value=""transparent"">" & _
                                                             "<param name=""FlashVars"" value=""textcolor=gray&timezoneoffset=" & MyTime & """>" & _
                                                             "<param name=""quality"" value=""high"">" & _
                                                             "<param name=""devicefont"" value=""true"">" & _
                                                             "<param name=""allowscriptaccess"" value=""samedomain"">" & _
                                                             "<embed type=""application/x-shockwave-flash"" pluginspage = ""http://www.macromedia.com/go/getflashplayer"" width=""90"" height=""90"" name=""analog_clock"" src=""" & Me.SrcDir & "/analog_clock.swf"" bgcolor=""#DDDDDD"" quality=""high"" swLiveConnect=""true"" allowScriptAccess=""samedomain"" FlashVars=""textcolor=gray&timezoneoffset=" & MyTime & """></embed>" & _
                                                             "</object>"
            Catch ex As Exception
                strClock = ""
            End Try

            Return strClock
        End Function

        Private Function ShowClock(ByVal MyTime As String) As String
            Dim strClock As String
            Try
                strClock = ""

                Dim sb As StringBuilder = New StringBuilder("")

                sb.Append(vbCrLf & "<script language=""JavaScript"">" & vbCrLf)
                sb.Append("<!--" & vbCrLf)
                sb.Append("// please keep these lines on when you copy the source" & vbCrLf)
                sb.Append("// made by: Nicolas - http://www.javascript-page.com" & vbCrLf)
                sb.Append("var clockID = 0;" & vbCrLf)
                sb.Append("function UpdateClock() {if(clockID) {clearTimeout(clockID);clockID  = 0;}var tDate = new Date(); var tHour = tDate.getHours(); if (tHour < 10){tHour =""0""+tHour;} var tMinute = tDate.getMinutes(); if (tMinute < 10){tMinute =""0""+tMinute;} var tSecond = tDate.getSeconds(); if (tSecond < 10){tSecond = ""0""+tSecond;} document.getElementById('" & ModID.ToString & "_pncClock').firstChild.nodeValue = tHour + "":"" + tMinute + "":"" + tSecond;clockID = setTimeout('UpdateClock()', 1000);}" & vbCrLf)
                sb.Append("function StartClock() {clockID = setTimeout('UpdateClock()', 500);}" & vbCrLf)
                sb.Append("function KillClock() {if(clockID) {clearTimeout(clockID);clockID  = 0;}}" & vbCrLf)
                sb.Append("//-->" & vbCrLf)
                sb.Append("</script>" & vbCrLf)
                sb.Append("<span id=""" & ModID.ToString & "_pncClock"">" & Now().ToLongTimeString & "</span>" & vbCrLf)
                sb.Append("<script language=""JavaScript"">" & vbCrLf)
                sb.Append("StartClock();" & vbCrLf)
                sb.Append("</script>" & vbCrLf)

                strClock = sb.ToString

            Catch ex As Exception
                strClock = ""
            End Try

            Return strClock
        End Function

#End Region

    End Class

End Namespace
